import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui;

import 'package:flutter/services.dart';
import 'package:palette_generator/palette_generator.dart';
import 'package:path_provider/path_provider.dart';

import '../model/image_picker.dart';

class NDWaterMarkerUtil {
  static Future<T> watermark<T>(
      List<dynamic>? data,
      List<ImageWaterMarkSettingModel> waterMarkSettings,
      bool isWaterMarkTiled) async {
    List<String> outPathList = [];
    Completer<T> completer = Completer();
    if (data?.isNotEmpty == true) {
      var directory = await getTemporaryDirectory();
      Future.wait<String>(data!
              .map((element) => _process(element['localPath'],
                  waterMarkSettings, directory.path, outPathList,
                  targetWidth: element['width'] is double
                      ? (element['width'] as double) ~/ 1
                      : element['width'],
                  targetHeight: element['height'] is double
                      ? (element['height'] as double) ~/ 1
                      : element['height'],
                  isWaterMarkTiled: isWaterMarkTiled))
              .toList())
          .then((value) {
        if (T == String) {
          completer.complete(outPathList.first as T);
        } else {
          completer.complete(outPathList as T);
        }
      });
    } else {
      completer.complete();
    }
    return completer.future;
  }

  static Future<String> _process(
      String tempPath,
      List<ImageWaterMarkSettingModel> waterMarkSettings,
      String directory,
      List<String> outPathList,
      {int? targetWidth,
      int? targetHeight,
      int limitSize = 1024,
      double scaleRatio = 0.5,
      bool isWaterMarkTiled = false}) async {
    Completer<String> completer = Completer();

    ///分割原图片绝对路径
    var splitPaths = tempPath.split("/");

    ///获取文件名
    if (splitPaths.isNotEmpty && splitPaths.last.contains(".")) {
      ///生成 TemporaryDirectory/markcache/原文件名 中间文件
      var outFile = File("$directory/${splitPaths.last}");
      var originImage = await _any2Image(ConvertToImageType.FILE,
          path: tempPath,
          width: targetWidth,
          height: targetHeight,
          scaleRatio: ((targetWidth ?? 0) > limitSize ||
                  (targetHeight ?? 0) > limitSize)
              ? scaleRatio
              : 1);

      List<dynamic> waterMarkList = [];

      var res = await PaletteGenerator.fromImage(originImage);
      PaletteColor? paletteColor = res.dominantColor;

      if (!isWaterMarkTiled) {
        ///如果不是平铺显示水印
        ///根据配置生成配置数量相等的水印
        for (var element in waterMarkSettings) {
          var picture = await _generatePicture(element, paletteColor?.color);
          waterMarkList.add(picture);
        }
      } else {
        ///如果是平铺显示水印
        ///首先根据图片宽度分为四等份
        waterMarkSettings[0].textWidth = originImage.width / 4;

        ///根据配置第一项生成水印（为了获取水印渲染高度）
        var paragraph =
            await _generatePicture(waterMarkSettings[0], paletteColor?.color);

        ///startPosition 水印第一个元素与x、y轴间距
        ///verticalDistance 水印行y轴间距
        const startPosition = 25, verticalDistance = 200;
        late num height;

        ///获取图片高度
        if (paragraph is ui.Paragraph) {
          height = paragraph.height ~/ 1;
        } else {
          height = paragraph.height;
        }

        ///计算图片可以配置多少行水印（当配置的角度非0时，由于倾斜的问题，可能会导致边角出现空余，所以角度不为0时，需要多渲染一行）
        var heightNumber = waterMarkSettings[0].radians != 0
            ? (originImage.height - height) ~/ (height + verticalDistance) + 2
            : (originImage.height - height) ~/ (height + verticalDistance) + 1;
        ImageWaterMarkSettingModel tempSetting = waterMarkSettings[0];
        waterMarkSettings.clear();
        double widthUnit = originImage.width / tempSetting.divided;
        double heightUnit = originImage.height / tempSetting.divided;

        ///一行默认渲染4个
        for (var i = -1; i < 5; i++) {
          for (var j = 0; j < heightNumber; j++) {
            ImageWaterMarkSettingModel settings = ImageWaterMarkSettingModel();
            settings.asset = tempSetting.asset;
            settings.divided = tempSetting.divided;
            settings.radians = tempSetting.radians;
            settings.text = tempSetting.text;
            settings.textStyle = tempSetting.textStyle;
            settings.textWidth = (originImage.width - startPosition) / 4;
            settings.type = tempSetting.type;

            ///由于要形成交错效果，所以每行的间距都会出现一段距离
            settings.dx = (startPosition +
                    i * ((originImage.width - startPosition) / 2) +
                    (j % 2 == 0
                            ? 0
                            : 0 - (originImage.width - startPosition) / 2) /
                        2) /
                widthUnit;
            settings.dy =
                (startPosition + j * (height + verticalDistance)) / heightUnit;

            ///如果是隔行，且为第一个元素，且角度为0，则该元素不需要渲染
            if (j % 2 != 0 && i == 0 && settings.radians == 0) continue;
            var picture = await _generatePicture(settings, paletteColor?.color);
            waterMarkList.add(picture);
            waterMarkSettings.add(settings);
          }
        }
      }

      ///再创建canvas
      ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
      ui.Canvas canvas = ui.Canvas(pictureRecorder);
      ui.Paint paint = ui.Paint();

      ///原图大小绘制在最底层
      canvas.drawImage(originImage, ui.Offset.zero, paint);

      ///将水印绘制到canvas
      ///此处两次循环说明：
      ///canvas如果在生成后，出现异步函数时，canvas会丢失PictureRecorder，导致绘制时出现Object has bean dispose错误
      ///必须先生成对应内容后，再生成canvas统一绘制
      for (var i = 0; i < waterMarkSettings.length; i++) {
        double widthUnit = originImage.width / waterMarkSettings[i].divided;
        double heightUnit = originImage.height / waterMarkSettings[i].divided;
        canvas.rotate(-waterMarkSettings[i].radians);
        if ((waterMarkList[i]) is ui.Paragraph) {
          ///canvas以左上角点为原点建立坐标系
          //以下出现2次rotate，因为旋转canvas后，整个画布坐标系会一起旋转，如不重置回原来，则下一次旋转的角度将根据此次角度叠加
          canvas.drawParagraph(
              (waterMarkList[i]) as ui.Paragraph,
              Offset(widthUnit * waterMarkSettings[i].dx,
                  heightUnit * waterMarkSettings[i].dy));
        } else {
          canvas.drawImage(
              (waterMarkList[i]) as ui.Image,
              Offset(widthUnit * waterMarkSettings[i].dx,
                  heightUnit * waterMarkSettings[i].dy),
              paint);
        }
        canvas.rotate(waterMarkSettings[i].radians);
      }

      ///生成与原图尺寸一样的合成图片
      ui.Image generateImage = await pictureRecorder
          .endRecording()
          .toImage(originImage.width, originImage.height);
      ByteData? data =
          await generateImage.toByteData(format: ui.ImageByteFormat.png);
      _dataWrite(data, outFile.path).then((value) {
        outPathList.add(outFile.path);
        originImage.dispose();
        completer.complete(outFile.path);
      }).whenComplete(() {
        generateImage.dispose();
      });
    } else {
      outPathList.add('');
      completer.complete('');
    }
    return completer.future;
  }

  ///数据转文件
  static Future<dynamic> _dataWrite(ByteData? data, String path) async {
    var outFile = File(path);
    await outFile.writeAsBytes(data!.buffer.asUint8List());
    return path;
  }

  ///文件转图片
  static Future<ui.Image> _any2Image(ConvertToImageType type,
      {String? path,
      Uint8List? data,
      int? width,
      int? height,
      double scaleRatio = 0.5}) async {
    late Uint8List codecData;
    if (type == ConvertToImageType.DATA) {
      codecData = data!;
    } else {
      codecData = File(path!).readAsBytesSync();
    }
    late ui.Codec codec;
    if (width != null && width != 0 && height != null && height != 0) {
      codec = await ui.instantiateImageCodec(codecData,
          targetWidth: width * scaleRatio ~/ 1,
          targetHeight: height * scaleRatio ~/ 1);
    } else {
      codec = await ui.instantiateImageCodec(codecData);
    }
    ui.FrameInfo fi = await codec.getNextFrame();
    codec.dispose();
    return fi.image;
  }

  static Future _generatePicture(
      ImageWaterMarkSettingModel settingModel, Color? mainColor) async {
    if (settingModel.type == WaterMarkType.TEXT) {
      return _generateText(settingModel, mainColor);
    } else {
      ByteData? data = await rootBundle.load(settingModel.asset!.keyName);
      return await _any2Image(ConvertToImageType.DATA,
          data: data.buffer.asUint8List());
    }
  }

  static ui.Paragraph _generateText(
      ImageWaterMarkSettingModel settingModel, Color? mainColor) {
    var i = 0;
    if ((mainColor?.red ?? 0) < 128) {
      i++;
    }
    if ((mainColor?.blue ?? 0) < 128) {
      i++;
    }
    if ((mainColor?.green ?? 0) < 128) {
      i++;
    }
    final paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle())
      ..pushStyle(settingModel.textStyle ??
          ui.TextStyle(
              fontSize: settingModel.fontSize,
              color: i >= 2 ? settingModel.lightColor : settingModel.darkColor))
      ..addText(settingModel.text ?? '');
    var paragraph = paragraphBuilder.build();
    paragraph.layout(ui.ParagraphConstraints(width: settingModel.textWidth));
    return paragraph;
  }
}
